查看原文
其他

数据呈现丨好用易懂的matplotlib可视化,快来了解一下!

数据Seminar 2021-06-03

去年12月,博主Selva Prabhakaran在自己运营的机器学习网站Machine Learning Plus上发布了博文:Top 50 matplotlib Visualizations – The Master Plots (with full python code)。这篇博文非常优秀,详细介绍了相关图、偏差图、排列图等七类图形共50幅图的绘制,但是——

  • 代码有一定难度,属于可视化进阶内容

  • 全英文,且没有任何图形解释

基于此,我们在此选出几幅具有代表性的图进行详细介绍(不仅仅是翻译,更有非常详细的注解),以飨读者。



导入绘图库&基本设置

对于日常数据可视化,在追求美的同时我们更应注重图表信息的有效传达,有效图表应该满足以下几点:

  • 在不歪曲事实的情况下传达正确和必要的信息。

  • 设计简单,可以轻易理解图表传达的意思。

  • 审美角度支持信息而不是掩盖信息。

  • 没有冗余信息。

首先,在准备画图之前,需要导入绘图包以及必要的一些设置。

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
import warnings; warnings.filterwarnings(action='once')  #忽略警告,"once"表示只打印第一次出现的匹配警告

large = 22; med = 16; small = 12
params = {'axes.titlesize': large,
         'legend.fontsize': med,
         'figure.figsize': (16, 10),
         'axes.labelsize': med,
         'axes.titlesize': med,
         'xtick.labelsize': med,
         'ytick.labelsize': med,
         'figure.titlesize': large}
plt.rcParams.update(params)
plt.style.use('seaborn-whitegrid')
sns.set_style("white")
%matplotlib inline  
#在jupyter notebook作图需要此行命令,用以显示图像

plt.rcParams['font.sans-serif'] = ['SimHei']    #显示中文
plt.rcParams['axes.unicode_minus']=False        #用来正常显示负号

# 检查版本
print(mpl.__version__)  # > 3.0.0 版本不足请更新,下同
print(sns.__version__)  # > 0.9.0

温馨提醒:在具体绘图前,请熟悉下每幅图所用的数据集。



散点图


散点图可判断两变量之间是否存在某种关联或总结坐标点的分布模式,是用于研究两个变量之间关系的经典图。如果数据中有多个组别,则可能需要以不同颜色可视化每个组。在matplotlib,可以使用plt.scatterplot()方便地执行此操作。

绘制中西部城市对应的人口数量

# 导入数据&认识数据
midwest = pd.read_csv("https://raw.githubusercontent.com/selva86/datasets/master/midwest_filter.csv")

# 准备数据
# 每一个分类对应一种颜色,plt.cm.tab10()是用于创建颜色的十号光谱
categories = np.unique(midwest['category'])   #城市种类
colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))] # 选取颜色

# 利用循环画出每个类别的散点图
plt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')    # 设置画布大小,分辨率、画布颜色(默认为白)、边框颜色(默认为黑)

for i, category in enumerate(categories):
   plt.scatter('area', 'poptotal',   #area,poptotal分别为横纵坐标数据
               data=midwest.loc[midwest.category==category, :],  #分别选出每个类别的数据
               s=20,      #设置点的大小
               cmap=colors[i],  #原文此处为c,color的简写,最好改成cmap
               label=str(category) #图例
              )

# 对图像进行装饰
plt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000),  #获取图像,设置x轴、y轴坐标的范围
             xlabel='Area', ylabel='Population') #获取图像,设置x轴、y轴标题

plt.xticks(fontsize=12);  #设置x轴刻度大小
plt.yticks(fontsize=12)   #设置x轴刻度大小
plt.title("Scatterplot of Midwest Area vs Population", fontsize=22) #设置图像标题没,文字大小
plt.legend(fontsize=12)   #图例文字大小
plt.show()    #显示图像

tips

1、在jupyter notebook中,可以用分号“;”代替plt.show(),置于上一行代码末尾

2、enumerate() 函数用于将一个可遍历的数据对象组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。如

seq = ['one', 'two', 'three']

for  i,element in enumerate(seq):  

        print (i,element)

#返回结果:

0 one

1 two

2 three

3、在plt.cm.tab10()中输入任意浮点数,来提取出一种颜色,这种颜色会以元祖的形式返回,表示为四个浮点数组成的RGBA色彩空间或者三个浮点数组成的RGB色彩空间中的随机色彩


绘制的目标图像为:




带最佳线性拟合线的散点图

如果想了解两个变量如何相互改变,那么最佳拟合线就是常用的方法。下图显示了数据中各组之间最佳拟合线的差异。若要禁用分组并仅为整个数据集绘制一条最佳拟合线,需从下面的 sns.lmplot()调用删除 hue ='cyl'参数。

绘制cyl气缸数量为4、8的displ(发动机排量)与hwy(公路里程)的拟合图

# 导入数据集
df = pd.read_csv("https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv")
df_select = df.loc[df.cyl.isin([4,8]), :]    # 筛选出cyl为4和8的数据

# 作图
sns.set_style("white")    #设置图表样式为白色,共有darkgrid、whitegrid、dark、white和ticks五种,默认为darkgrid
gridobj = sns.lmplot(x="displ" #lmplot是一种集合基础绘图与基于数据建立回归模型的绘图方法,利用'hue'、'col'、'row'参数来控制绘图变量
                    , y="hwy"
                    , hue="cyl"   #hue用于分类
                    ,data=df_select  #数据
                    , height=7    #子图高度
                    , aspect=1.6   #控制图的长宽比为1.6
                    , robust=True   #设置抗噪声鲁棒性
                    , palette='tab10'  #调色
                    , scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))  #scatter_kws控制散点图参数,点的大小,点边缘宽度、边缘颜色,控制直线的参数为line_kws

# 装饰图像
gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))   # 设置横纵坐标
plt.title("Scatterplot with line of best fit grouped by number of cylinders", fontsize=20) #图像标题及大小
plt.show()

绘制的目标图像为:



偏差图--发散型条形图

如果想根据单个指标查看项目的变化情况,并可视化此差异的顺序和数量,那么发散型条形图(Diverging Bars)是一个很好的工具。它有助于快速区分数据中组的性能,并且非常直观。

绘制不同汽车每加伦汽油能距的英里数(标准化后)

# 导入数据&处理数据
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/mtcars.csv")
x = df.loc[:, ['mpg']]    #提取出列名为mpg(每加伦汽油能距的英里数)的数据
df['mpg_z'] = (x - x.mean())/x.std()    #z-score标准化
df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]  #利用列表推导式分别赋予正负值不同的颜色
df.sort_values('mpg_z', inplace=True)  #对标准化后的数据排序
df.reset_index(inplace=True)    #重设索引

# 作图
plt.figure(figsize=(14,10), dpi= 80)
plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=5)
#plt.hlines画横条形图,相应地画竖条形图可用plt.vlines,alpha为色彩透明度
for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z):
   t = plt.text(x, y, round(tex, 2), horizontalalignment='right' if x < 0 else 'left',  
                verticalalignment='center', fontdict={'color':'red' if x < 0 else 'green', 'size':14})    #在条形图上显示具体数字,设置位置颜色大小
   
# 装饰图像
plt.gca().set(ylabel='$Model$', xlabel='$Mileage$')   #美元符号“$”的作用是使字体成为斜体
plt.yticks(df.index, df.cars, fontsize=12)
plt.title('Diverging Bars of Car Mileage', fontdict={'size':20})
plt.grid(linestyle='--', alpha=0.5)   # 绘制网格线,线地形状为“--”
plt.show()

绘制的目标图像为:



有序条形图

有序条形图可以有效地传达项目的排名顺序,若在图表上方添加度量标准的值,用户还可以从图表本身获取精确信息。

绘制不同汽车品牌的城市里程/加仑

# 导入数据
df_raw = pd.read_csv("https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv")
df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())  #聚合每个品牌的车辆,并求每个品牌城市里程/加仑的均值
df.sort_values('cty', inplace=True)   # 排序
df.reset_index(inplace=True)   #重设索引,将manufacturer重新设为列名

# 作图
import matplotlib.patches as patches   #导入matplotlib.patches,可以用这个模块中的函数创造各种几何对象

fig, ax = plt.subplots(figsize=(16,10), facecolor='white', dpi= 80)  # 生成两个对象fig,ax
ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=20)

# 在条形图上添加数据
for i, cty in enumerate(df.cty):  #因每次只能加一个,故用循环
   ax.text(i   #注释文字的横坐标
          , cty+0.5  #注释文字的纵坐标
          , round(cty, 1)   #保留一位小数
          , horizontalalignment='center')   #注释文字显示的位置


# 设置标题、标签、坐标
ax.set_title('城市里程柱状图', fontdict={'size':22})  #设置标题,字体大小
ax.set(ylabel='城市公里/加仑', ylim=(0, 30))    #y轴标题及刻度
plt.xticks(df.index     #显示在横坐标上的位置
          , df.manufacturer.str.upper()   #刻度显示的内容,大写
          , rotation=60   # 旋转角度
          , horizontalalignment='right'  # 相对文字的位置
          , fontsize=12)

# 在X轴上添加色块(长方形)
p1 = patches.Rectangle((.57, -0.005) # 元组,长方形所在的左下角的坐标,注意该坐标不是以横纵坐标为基准的
                      , width=.33  # 长方形的宽(长)
                      , height=.13  # 长方形的高(宽)
                      , alpha=.1  # 透明度
                      , facecolor='green'  # 矩形的颜色
                      , transform=fig.transFigure  # 确保矩形显示再图像的最上方,不会被子图挡住
                    )
p2 = patches.Rectangle((.124, -0.005), width=.446, height=.13, alpha=.1, facecolor='red', transform=fig.transFigure)
fig.add_artist(p1)  # 注意是对画布进行操作
fig.add_artist(p2)
plt.show()

绘制的目标图像为:



密度图

密度图是一种常用工具,用于可视化连续变量的分布。通过“响应”变量对它们进行分组,您可以检查X和Y之间的关系。

以下示例用以描述城市里程的分布如何随着汽缸数的变化而变化。

绘制不同气缸数量的汽车的城市里程/加仑密度图

# 导入数据
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv")

# 作图,sns.kdeplot()拟合并绘制单变量或双变量和密度估计值
plt.figure(figsize=(16,10), dpi= 80)
sns.kdeplot(df.loc[df['cyl'] == 4, "cty"]   #选出cyl(气缸数量)为4的cty(城市里程/加仑)数值
          , shade=True  #填充曲线下阴影的控制参数
          , color="g"  #颜色
          , label="Cyl=4" #图例
          , alpha=.7  #透明度
          )
sns.kdeplot(df.loc[df['cyl'] == 5, "cty"], shade=True, color="deeppink", label="Cyl=5", alpha=.7)
sns.kdeplot(df.loc[df['cyl'] == 6, "cty"], shade=True, color="dodgerblue", label="Cyl=6", alpha=.7)
sns.kdeplot(df.loc[df['cyl'] == 8, "cty"], shade=True, color="orange", label="Cyl=8", alpha=.7)

# 装饰图像
plt.title('Density Plot of City Mileage by n_Cylinders', fontsize=22)
plt.legend()
plt.show()

绘制的目标图像为:



人口金字塔图

人口金字塔可用于显示由数量排序的组的分布。或者它也可以用于显示人口的逐级过滤,因为它在下面用于显示有多少人通过营销渠道的每个阶段。

绘制人口金字塔图

# 导入数据
df = pd.read_csv("https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv")

# 作图
plt.figure(figsize=(13,10), dpi= 80)
group_col = 'Gender'
order_of_bars = df.Stage.unique()[::-1]   #倒序排列Stage
colors = [plt.cm.Spectral(i/float(len(df[group_col].unique())-1)) for i in range(len(df[group_col].unique()))]    #选取颜色

for c, group in zip(colors, df[group_col].unique()):
   sns.barplot(x='Users', y='Stage', data=df.loc[df[group_col]==group, :], order=order_of_bars, color=c, label=group)   #依次画出每个阶段的条形图

# 装饰图像    
plt.xlabel("$Users$")  #x轴标题,设为斜体
plt.ylabel("Stage of Purchase")  #y轴标题
plt.yticks(fontsize=12)  #刻度标尺字体大小
plt.title("Population Pyramid of the Marketing Funnel", fontsize=22)  #图像标题
plt.legend() #图例
plt.show()

►绘制的目标图像为:



华夫饼图

华夫饼图(Waffle Chart),或称为直角饼图,可以直观的描绘百分比完成比例情况。与传统的饼图相比较,华夫饼图表达的百分比更清晰和准确,它的每一个格子代表 1%。绘制华夫饼图需要安装pywaffle库

绘制不同类型的汽车的百分比华夫饼图(代码在原博主的基础上有所改动)

from pywaffle import Waffle    # 导入pywaffle库

# 导入数据
df_raw = pd.read_csv("https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv")

# 准备数据
# 按汽车类型分组
df_class = df_raw.groupby('class').size().reset_index(name='counts_class')  # 作图数据:按汽车类型分组
n_categories = df_class.shape[0]     # 共7类车
colors_class = [plt.cm.nipy_spectral(i/float(n_categories)) for i in range(n_categories)]   # 更易观察的颜色
labels_class = [n[1] for n in df_class.itertuples()]   # 比原文相对更加简单的写法来获取标签

# 按气缸数量分组
df_cyl = df_raw.groupby('cyl').size().reset_index(name='counts_cyl')  # 作图数据:按气缸数量分组
n_categories = df_cyl.shape[0]   # 共4类气缸
colors_cyl = [plt.cm.nipy_spectral(i/float(n_categories)) for i in range(n_categories)]   # 颜色
labels_cyl = [n[1] for n in df_cyl.itertuples()]  

# 按汽车品牌分组
df_make = df_raw.groupby('manufacturer').size().reset_index(name='counts_make')  # 作图数据:按汽车品牌分组
n_categories = df_make.shape[0]  # 共15类汽车品牌
colors_make = [plt.cm.tab20b(i/float(n_categories)) for i in range(n_categories)]  # 颜色
labels_make = [n[1] for n in df_make.itertuples()]  

# Draw Plot and Decorate
fig = plt.figure(
   FigureClass=Waffle,   # 绘制华夫饼图
   plots={    #绘制多个图像,里面为键值对
       '311': {    #这里的数字表示3行1列,索引为1的图
           'values': df_class['counts_class'],   # 数据集
           'labels': labels_class,   #标签
           'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Class'},  #图例位置
           'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize':18},  #设置图像标题
           'colors': colors_class  #设置颜色
      },
       '312': {   # 3行1列第2幅图
           'values': df_cyl['counts_cyl'],
           'labels': labels_cyl,  
           'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Cyl'},
           'title': {'label': '# Vehicles by Cyl', 'loc': 'center', 'fontsize':18},
           'colors': colors_cyl
      },
       '313': {  # 3行1列第3幅图
           'values': df_make['counts_make'],
           'labels': labels_make,  
           'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Manufacturer'},
           'title': {'label': '# Vehicles by Make', 'loc': 'center', 'fontsize':18},
           'colors': colors_make
      }
  },
   rows=9,   # 华夫饼有几行,可以单独放入相应图像
   figsize=(16, 14),  # 图像大小
   icons = 'car',icon_size = 25, # 使用icons,可以用不同的形状,方块变成车,还可以‘child’,'sun'
   icon_legend = True # 让图例也显示icons的图形
)

tips

1、在jupyter notebook可以直接输入%%cmd;pip install pywaffle安装pywaffle库

2、bbox_to_anchor:一种更加精确显示图像位置的方式,当bbbox_to_anchor参数存在时,会以参数中输入的坐标为参考点,再考虑loc来设置。bbox_to_anchor = (0.5,0.5)中输入两个数值表示横纵坐标的一半的意思,bbox_to_anchor = (0.5,0.5,0.1,0.4) 中,前两个数字为坐标,后边两个数字分别为宽和高

3、该图显示的汽车图标,需要另行下载图标文件(下载地址:https://fontawesome.com/download),解压后将下载的3个OTF文件置于报错警告下的文件目录(font文件夹)中:

下载页面

解压的OTF文件:

小编的报错信息(OTF文件放置位置)


绘制的目标图像为:



双纵坐标图


如果要显示在同一时间点测量两个不同数量的两个时间序列,则可以在右侧的辅助Y轴上再绘制第二个系列。

绘制不同年份的个人储蓄率与失业人数

#导入数据
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv")

x = df['date']       #X轴数据:时间
y1 = df['psavert']   #y1数据:个人储蓄率
y2 = df['unemploy']  #y2数据:失业人数

#绘制第一条曲线,以左侧为Y轴
fig, ax1 = plt.subplots(1,1,figsize=(16,9), dpi= 80)  # 一行一列,生成一个画布和一个子图
ax1.plot(x, y1, color='tab:red')   #画出时间与个人储蓄率的图像

#绘制第二条曲线,以右侧为Y轴
ax2 = ax1.twinx()  # 共享相同的x轴,并带次坐标的另一个子图
ax2.plot(x, y2, color='tab:blue') #画出时间与失业人数的图像

#装饰图像
#第一个子图(左纵轴)
ax1.set_xlabel('年份', fontsize=20)   #设置横坐标的标题及字体大小
ax1.tick_params(axis='x', rotation=0, labelsize=12) #设置与x标尺有关的参数,rotation为x轴标签的旋转角度,默认为0,labelsize为标签显示大小
ax1.set_ylabel('个人储蓄率', color='tab:red', fontsize=20) #设置y轴标题,颜色,字体大小
ax1.tick_params(axis='y', rotation=0, labelcolor='tab:red' ) #设置与y1标尺有关的参数
ax1.grid(alpha=.4) #设置网格的透明度,默认为1

# 第二个子图(右纵轴)
ax2.set_ylabel("失业人数", color='tab:blue', fontsize=20)  # 设置y2轴的标题,颜色,字体大小
ax2.tick_params(axis='y', labelcolor='tab:blue') #设置y2轴标签颜色
ax2.set_xticks(np.arange(0, len(x), 60)) #标尺显示的间距为60个月
ax2.set_xticklabels(x[::60], rotation=90, fontdict={'fontsize':10}) #横坐标显示的具体内容,旋转90度,字号10
ax2.set_title("个人储蓄率 VS 失业人数", fontsize=22) #设置标题
fig.tight_layout() #自动调整子图参数,使之填充整个图像区域
plt.show()

►绘制的目标图像为:

tips

该图在设置颜色时用的是color='tab:blue',这其实是matplotlib设置颜色的一种方法而已,详情可见:https://matplotlib.org/users/colors.html


通过上面一些图形画法的讲解,想必有些读者对Python作图有了一定的了解,当然上面只是大佬博客的部分内容,感兴趣的话可以点击阅读原文观摩观摩,相信肯定会有所很大的收获。



你要的工具&方法我都给你整理好了!

工具&方法丨关于T检验和F检验你不得不知道的二三事

工具&方法丨菜鸟升级打怪系列之python代码优化(1)

工具&方法丨老姚趣谈如何理解假设检验的逻辑

工具&方法 | 权威华人教授为您解惑:如何用好经济模型讲述“中国故事”?

工具&方法 | 小刘老师“再”出新招:JSON数据转为面板数据

工具&方法 | 教授教你如何用DID和DDD模型做政策评估

工具&方法 | 用Python3处理数据:“import”可以这样自由地调度函数?(内附代码)


听说你还在为数据呈现烦恼?看这里!

数据呈现丨想要数据可视化?你还少了它!

数据呈现 | Stata+R+Python:拨开数据迷雾,窥探可视化之“美”(工具书推荐,附PDF资源链接)

数据呈现 |Stata+R+Python:一文帮你解决Paper、PPT中的数据可视化问题


让我猜猜,或许你需要的还有这些!

学术前沿丨机器学习能成为因果推断的“圣杯”吗?

学术前沿 | 规律与因果:大数据对社会科学研究冲击之反思

资源推荐 | 徐现祥教授团队 IRE 公开数据:官员交流、方言指数等

特别推荐 | 专利引用数据,可以用来做哪些研究?




数据Seminar

这里是大数据、分析技术与学术研究的三叉路口





出处:Machine Learning Plus
翻译&注解:企研数据 · 威武哥

编辑:青酱





    欢迎扫描👇二维码添加关注    


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存